package com.daiyang.campus_lucky_draw.service.mq;

import cn.hutool.core.date.DateUtil;
import com.daiyang.campus_lucky_draw.common.enums.ActivityPrizeStatusEnum;
import com.daiyang.campus_lucky_draw.common.enums.ActivityPrizeTiersEnum;
import com.daiyang.campus_lucky_draw.common.enums.ActivityStatusEnum;
import com.daiyang.campus_lucky_draw.common.enums.ActivityUserStatusEnum;
import com.daiyang.campus_lucky_draw.common.exception.ServiceException;
import com.daiyang.campus_lucky_draw.common.utils.JacksonUtil;
import com.daiyang.campus_lucky_draw.common.utils.MailUtil;
import com.daiyang.campus_lucky_draw.common.utils.RedisUtil;
import com.daiyang.campus_lucky_draw.common.utils.SMSUtil;
import com.daiyang.campus_lucky_draw.contreller.param.DrawPrizeParam;
import com.daiyang.campus_lucky_draw.mapper.ActivityPrizeMapper;
import com.daiyang.campus_lucky_draw.mapper.DO.ActivityPrizeDO;
import com.daiyang.campus_lucky_draw.mapper.DO.WinningRecordDO;
import com.daiyang.campus_lucky_draw.mapper.WinningRecordMapper;
import com.daiyang.campus_lucky_draw.service.DrawPrizeService;
import com.daiyang.campus_lucky_draw.service.activitystatus.ActivityStatusManager;
import com.daiyang.campus_lucky_draw.service.dto.ConvertActivityStatusDTO;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.daiyang.campus_lucky_draw.config.DirectRabbitConfig.QUEUE_NAME;

@Slf4j
@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceiver {
    private static final Logger logger =
            LoggerFactory.getLogger(MqReceiver.class);
    private static final String WINNING_TEMPLATE_CODE = "SMS_465985911";
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private MailUtil mailUtil;
    @Autowired
    private SMSUtil smsUtil;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private DrawPrizeService drawPrizeService;
    @Autowired
    private ActivityStatusManager activityStatusManager;
    @Autowired
    private ThreadPoolTaskExecutor asyncServiceExecutor;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
//    @Autowired
//    private ThreadPoolTaskExecutor asyncServiceExecutor;
    @RabbitHandler
    public void process(Map<String, Object> message) {
        logger.info("DirectReceiver消费者收到消息 : " + message.toString());

        String msgData = (String)message.get("messageData");
        DrawPrizeParam param = JacksonUtil.readValue(msgData, DrawPrizeParam.class);

        

        
        try {
            // 1、核对抽奖信息有效性
            if( !drawPrizeService.checkDrawPrizeValid(param)) {
                return;
            }
            // 2、扭转活动状态
            convertStatus(param);
//            // 3、保存中奖结果
            List<WinningRecordDO> winningRecordDOS  =
                    drawPrizeService.saveWinnerRecords(param);
//          // 通知中奖者（邮箱、短信）
          syncExecute(winningRecordDOS);
          

        } catch (ServiceException e) {
            logger.error("mq消息处理异常：{}", e.getCode(), e);
            // 异常回滚中奖结果+活动/奖品状态，保证事务⼀致性
            rollback(param);
        } catch (Exception e) {
            logger.error("mq消息处理异常：", e);
            // 异常回滚中奖结果+活动/奖品状态，保证事务⼀致性
            rollback(param);
        }
    }

    private void rollback(DrawPrizeParam param) {
        // 1、判断活动/奖品/⼈员状态是否扭转成功
        if (!statusNeedRollback(param)) {
            return;
        }
        // 需要回滚: 回滚
        rollbackStatus(param);


        //2回滚中奖名单
        //是否需要回滚
        if(!winnrNeedRollback(param)) {
            return;
        }
        // 需要
        rollbackWinner(param);
    }

    private void rollbackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(), param.getPrizeId());
    }

    private boolean winnrNeedRollback(DrawPrizeParam param) {
        int count =winningRecordMapper.countByAPId(param.getActivityId(),param.getPrizeId());

        return count>0;

    }

    private void rollbackStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIds(
                param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);
    }

    private boolean statusNeedRollback (DrawPrizeParam param) {
        // 逻辑：
        // 1. ⾸先扭转状态⽅法⾃⾝保证了事务⼀致性，因此在这⾥我们只⽤判断活动/奖品/⼈员其⼀是否扭转成功即可。
        // 2. 不⽤判断活动是否扭转成功，因为活动不⼀定要被扭转（依赖奖品）。
        // 结论：判断奖品是否抽完即可。
        ActivityPrizeDO activityPrizeDO =

                activityPrizeMapper.selectByAPId(param.getActivityId(),
                        param.getPrizeId());
        return
                activityPrizeDO.getStatus().equals(ActivityPrizeStatusEnum.COMPLETED.name());
    }




    private void syncExecute(List<WinningRecordDO> winningRecordDOS) {
        // execute不会抛异常影响主线程，所以这⾥不⽤抓异常
       asyncServiceExecutor.execute(() -> pushWinningList(winningRecordDOS));
        // asyncServiceExecutor.execute(() -> sendMessage(winningRecordDOS));
    }

    private void pushWinningList(List<WinningRecordDO> recordDOList) {
        if(CollectionUtils.isEmpty(recordDOList)) {
            logger.warn("中奖名单为空！");
            return;
        }
        recordDOList.forEach(record -> {
            String context = "Hi，" + record.getWinnerName() + "。恭喜你在"
                    + record.getActivityName() + "活动中获得"
                    +
                    ActivityPrizeTiersEnum.fromName(record.getPrizeTier()).getMessage()
                    + "：" + record.getPrizeName() + "。获奖时间为"
                    + DateUtil.formatTime(record.getWinningTime()) + "，请尽快领 取您的奖励！";
            mailUtil.sendSampleMail(record.getWinnerEmail(),
                    "中奖通知",
                    context);
        });
    }

    private void sendMessage(List<WinningRecordDO> winningRecordDOS) {
        if(CollectionUtils.isEmpty(winningRecordDOS)) {
            logger.warn("中奖名单为空！");
            return;
        }
        winningRecordDOS.forEach(record -> {
            Map<String, String> templateParam = new HashMap<>();
            templateParam.put("name", record.getWinnerName());
            templateParam.put("activityName", record.getActivityName());
            templateParam.put("prizeTiers", ActivityPrizeTiersEnum.fromName(
                    record.getPrizeTier()).getMessage());
            templateParam.put("prizeName", record.getPrizeName());
            templateParam.put("winningTime",
                    DateUtil.formatTime(record.getWinningTime()));
            smsUtil.sendMessage(WINNING_TEMPLATE_CODE,
                    record.getWinnerPhoneNumber().getValue(),
                    JacksonUtil.writeValueAsString(templateParam));
        });
    
    }

    private void convertStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO   convertActivityStatusDTO=new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(param
                .getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.COMPLETED);
        activityStatusManager.handlerEvent(convertActivityStatusDTO);

    }

//    private void convertStatus(DrawPrizeParam param) {
//        //问题：活动状态扭转有依赖性，现在只依赖人员和奖品后续如果添加新的依赖,代码不容易进行改动。
//        //结局按方案：设计模式(责任链模式 策略模式)
//        //人员状态扭转：INIT ->COMPLETED
//        //奖品状态扭转: INIT ->COMPLETED
//        //活动状态扭转(依赖人员和奖品状态，必须人员状态和活动状态扭转完，才可以扭转)RUNNING ->COMPLETED
//
//        //1 扭转奖品状态
//        // 查询活动关联奖品信息
//        //条件判断是否符合扭转奖品状态，判断当前状态是否COMPLETE ,如果是不要扭转（其实在checkDrawPrizeValid中已经过滤过了）
//
//        //查活动关人员品信息
//        //条件判断是否符合扭转奖品状态，判断当前状态是否COMPLETE ,如果是不要扭转（其实在checkDrawPrizeValid中已经过滤过了）
//        //才去扭转
//        //3 扭转活动状态(必须再扭转奖品状态之后完成)
//        //查活动信息
//        //条件判断是否符合扭转奖品状态，判断当前状态是否COMPLETE ,如果是不要扭转（其实在checkDrawPrizeValid中已经过滤过了）
//        // 才去扭转
//    }
}